<fix>[storage]: return group tree in APIQueryVolumeSnapshotTree#4110
<fix>[storage]: return group tree in APIQueryVolumeSnapshotTree#4110zstack-robot-2 wants to merge 2 commits into
Conversation
概述本 PR 新增卷快照分组树功能,扩展 API 查询响应以支持快照分组的树形展示。通过添加新的数据模型、构建器和清理逻辑,完整实现分组树的构建、查询与删除管理。 变更内容卷快照分组树数据模型与 API 集成
预计审查工作量🎯 3 (中等) | ⏱️ ~25 分钟 诗
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
f8a7ff5 to
0af2187
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@storage/src/main/java/org/zstack/storage/snapshot/group/VolumeSnapshotGroupTreeBuilder.java`:
- Around line 213-229: The current winner-selection loop in
VolumeSnapshotGroupTreeBuilder uses the iteration order of the votes Map to pick
a winner on ties, causing nondeterministic parentGroupUuid; update the tie-break
so ties are resolved deterministically (e.g., compare group UUID strings
lexicographically and choose the smallest). Specifically, in the loop that
inspects votes (variable votes, winner, top, tie), when e.getValue() == top
instead of setting tie true only, compare e.getKey() with the current winner and
set winner to the lexicographically smaller UUID to ensure stable selection;
keep logging of tied votes but ensure the returned winner is deterministic.
In
`@storage/src/main/java/org/zstack/storage/snapshot/VolumeSnapshotManagerImpl.java`:
- Around line 1369-1384: The reply handler in VolumeSnapshotManagerImpl
currently only checks reply.getInventories() for null but not for an empty list,
letting the flow continue and potentially return groupTrees for queries that
should be filtered out; update the early-exit to return when
reply.getInventories() is null or empty (e.g., check
reply.getInventories().isEmpty()), so the subsequent calls to
groupTreeBuilder.extractVolumeUuidCondition(msg),
groupTreeBuilder.findRootVmUuid(volumeUuid) and
reply.setGroupTrees(groupTreeBuilder.buildForVm(vmInstanceUuid)) are only
executed when inventories contain results.
In
`@storage/src/main/java/org/zstack/storage/snapshot/VolumeSnapshotTreeBase.java`:
- Around line 1436-1441: The code in VolumeSnapshotTreeBase calls
dbf.removeByPrimaryKey(volumeSnapshotInv.getGroupUuid(),
VolumeSnapshotGroupVO.class) twice; remove the duplicate call so the group is
only deleted once. Locate the block where logger.debug(...),
vidm.deleteArchiveVmInstanceResourceMetadataGroup(volumeSnapshotInv.getGroupUuid()),
and the two dbf.removeByPrimaryKey(...) calls occur and delete the redundant
second dbf.removeByPrimaryKey invocation that uses
volumeSnapshotInv.getGroupUuid() and VolumeSnapshotGroupVO.class.
- Around line 2177-2185: Remove the unresolved merge markers (<<<<<<<, =======,
>>>>>>>) and merge the two branches: use the correctly scoped collection (use
groupsToDelete) and perform all three actions when it's non-empty — call
vidm.deleteArchiveVmInstanceResourceMetadataGroup for each UUID, invoke
cleanVmHostBackupFilesForGroup(groupsToDelete) to clean backup files, and then
call dbf.removeByPrimaryKeys(groupsToDelete, VolumeSnapshotGroupVO.class);
ensure you reference the existing methods
deleteArchiveVmInstanceResourceMetadataGroup, cleanVmHostBackupFilesForGroup,
and dbf.removeByPrimaryKeys and remove any mention of the undefined
groupUuids/touchedGroupUuids variables.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 6c20d837-6001-4372-8da5-1da694bd5bdf
📒 Files selected for processing (12)
header/src/main/java/org/zstack/header/storage/snapshot/APIQueryVolumeSnapshotTreeReply.javaheader/src/main/java/org/zstack/header/storage/snapshot/APIQueryVolumeSnapshotTreeReplyDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/storage/snapshot/group/VolumeSnapshotGroupTreeInventory.javaheader/src/main/java/org/zstack/header/storage/snapshot/group/VolumeSnapshotGroupTreeInventoryDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/storage/snapshot/group/VolumeSnapshotGroupTreeRefInventory.javaheader/src/main/java/org/zstack/header/storage/snapshot/group/VolumeSnapshotGroupTreeRefInventoryDoc_zh_cn.groovysdk/src/main/java/org/zstack/sdk/QueryVolumeSnapshotTreeResult.javasdk/src/main/java/org/zstack/sdk/VolumeSnapshotGroupTreeInventory.javasdk/src/main/java/org/zstack/sdk/VolumeSnapshotGroupTreeRefInventory.javastorage/src/main/java/org/zstack/storage/snapshot/VolumeSnapshotManagerImpl.javastorage/src/main/java/org/zstack/storage/snapshot/VolumeSnapshotTreeBase.javastorage/src/main/java/org/zstack/storage/snapshot/group/VolumeSnapshotGroupTreeBuilder.java
Add groupTrees field to APIQueryVolumeSnapshotTreeReply, built from VolumeSnapshotGroupVO topology with vote-majority parent resolution. Extract build logic into VolumeSnapshotGroupTreeBuilder. Resolves: ZSV-9792 Change-Id: I71616f6f6f6c637a6167637863727575746f616c
0af2187 to
094d2c3
Compare
Resolves: ZSV-9792 Change-Id: I84669169a926bf3fb6db8accaafb7d6d0751c0de
ee26371 to
bf0620b
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@storage/src/main/java/org/zstack/storage/snapshot/group/VolumeSnapshotGroupTreeBuilder.java`:
- Around line 267-281: In markCurrent (class VolumeSnapshotGroupTreeBuilder) the
current selection is non-deterministic when all
VolumeSnapshotGroupTreeInventory.getCreateDate() values are null because newest
falls back to the first Map iteration; modify markCurrent to first detect
whether any node has a non-null createDate and only setNewest/setCurrent when at
least one non-null exists OR implement a stable tie-breaker: compare createDate
first, and when both are null or equal use a deterministic key such as
node.getUuid() (lexicographic ascending) to choose newest before calling
setCurrent(true) on the chosen VolumeSnapshotGroupTreeInventory; ensure you
reference groupNodeMap and update only the single chosen node.
- Around line 180-193: 在 linkParents/assembleForest 的父子组装流程中缺少有向环检测,可能产生循环依赖;在把
parentGroupUuid 写回节点(方法 linkParents 使用 resolveParentGroupUuid)并在 assembleForest
挂子节点前先对由 parentMap/snapToGroup/refsByGroup 导出的有向图做环检测(例如使用 DFS 标记或 Kahn
拓扑排序),一旦检测到环就断开环中一条父边,按照建议规则选出要断开的边(比如在环内移除指向 createDate 较新的节点的父边,或在相同时间取 uuid
较大者断开),更新相应节点的 parentGroupUuid(和相关映射),然后再安全地调用 assembleForest 构建树。
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 7a73eb75-0dfa-413b-af11-3a653627f581
📒 Files selected for processing (9)
header/src/main/java/org/zstack/header/storage/snapshot/APIQueryVolumeSnapshotTreeReply.javaheader/src/main/java/org/zstack/header/storage/snapshot/APIQueryVolumeSnapshotTreeReplyDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/storage/snapshot/group/VolumeSnapshotGroupTreeInventory.javaheader/src/main/java/org/zstack/header/storage/snapshot/group/VolumeSnapshotGroupTreeInventoryDoc_zh_cn.groovyheader/src/main/java/org/zstack/header/storage/snapshot/group/VolumeSnapshotGroupTreeRefInventory.javaheader/src/main/java/org/zstack/header/storage/snapshot/group/VolumeSnapshotGroupTreeRefInventoryDoc_zh_cn.groovystorage/src/main/java/org/zstack/storage/snapshot/VolumeSnapshotManagerImpl.javastorage/src/main/java/org/zstack/storage/snapshot/VolumeSnapshotTreeBase.javastorage/src/main/java/org/zstack/storage/snapshot/group/VolumeSnapshotGroupTreeBuilder.java
✅ Files skipped from review due to trivial changes (1)
- header/src/main/java/org/zstack/header/storage/snapshot/group/VolumeSnapshotGroupTreeRefInventoryDoc_zh_cn.groovy
🚧 Files skipped from review as they are similar to previous changes (7)
- header/src/main/java/org/zstack/header/storage/snapshot/group/VolumeSnapshotGroupTreeRefInventory.java
- header/src/main/java/org/zstack/header/storage/snapshot/APIQueryVolumeSnapshotTreeReplyDoc_zh_cn.groovy
- header/src/main/java/org/zstack/header/storage/snapshot/group/VolumeSnapshotGroupTreeInventoryDoc_zh_cn.groovy
- header/src/main/java/org/zstack/header/storage/snapshot/group/VolumeSnapshotGroupTreeInventory.java
- header/src/main/java/org/zstack/header/storage/snapshot/APIQueryVolumeSnapshotTreeReply.java
- storage/src/main/java/org/zstack/storage/snapshot/VolumeSnapshotTreeBase.java
- storage/src/main/java/org/zstack/storage/snapshot/VolumeSnapshotManagerImpl.java
| private void linkParents(Map<String, VolumeSnapshotGroupTreeInventory> groupNodeMap, | ||
| Map<String, List<VolumeSnapshotGroupRefVO>> refsByGroup, | ||
| Map<String, String> parentMap, | ||
| Map<String, String> snapToGroup, | ||
| Map<String, Date> groupCreateDate) { | ||
| for (VolumeSnapshotGroupTreeInventory node : groupNodeMap.values()) { | ||
| String parentGroupUuid = resolveParentGroupUuid(node.getUuid(), | ||
| refsByGroup.getOrDefault(node.getUuid(), Collections.emptyList()), | ||
| parentMap, snapToGroup, groupCreateDate); | ||
| if (parentGroupUuid != null && groupNodeMap.containsKey(parentGroupUuid)) { | ||
| node.setParentGroupUuid(parentGroupUuid); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
父子链接缺少断环保护,可能返回非树结构
Line 185-191 直接写入 parentGroupUuid,Line 247-252 直接挂子节点,当前没有任何环检测。投票结果一旦出现 A→B→…→A,会让 forest 为空或形成循环 children,破坏 API 的“树”契约,后续序列化/遍历也有风险。
建议在组装前增加一次有向环检测并断环(例如在环内按规则移除一条边:较新 createDate 或较大 uuid 的节点断开父边),再进入 assembleForest。
Also applies to: 245-253
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@storage/src/main/java/org/zstack/storage/snapshot/group/VolumeSnapshotGroupTreeBuilder.java`
around lines 180 - 193, 在 linkParents/assembleForest 的父子组装流程中缺少有向环检测,可能产生循环依赖;在把
parentGroupUuid 写回节点(方法 linkParents 使用 resolveParentGroupUuid)并在 assembleForest
挂子节点前先对由 parentMap/snapToGroup/refsByGroup 导出的有向图做环检测(例如使用 DFS 标记或 Kahn
拓扑排序),一旦检测到环就断开环中一条父边,按照建议规则选出要断开的边(比如在环内移除指向 createDate 较新的节点的父边,或在相同时间取 uuid
较大者断开),更新相应节点的 parentGroupUuid(和相关映射),然后再安全地调用 assembleForest 构建树。
| private void markCurrent(Map<String, VolumeSnapshotGroupTreeInventory> groupNodeMap) { | ||
| VolumeSnapshotGroupTreeInventory newest = null; | ||
| for (VolumeSnapshotGroupTreeInventory node : groupNodeMap.values()) { | ||
| if (newest == null) { | ||
| newest = node; | ||
| continue; | ||
| } | ||
| if (node.getCreateDate() != null && (newest.getCreateDate() == null | ||
| || node.getCreateDate().after(newest.getCreateDate()))) { | ||
| newest = node; | ||
| } | ||
| } | ||
| if (newest != null) { | ||
| newest.setCurrent(true); | ||
| } |
There was a problem hiding this comment.
current 在 createDate 全为 null 时不稳定
Line 270-281 在所有节点 createDate == null 时,newest 会落到 HashMap.values() 的迭代首项,结果非确定,API 返回会抖动。
可改为:仅当至少存在一个非 null createDate 才标记 current,或补充稳定 tie-break(如 uuid 升序)。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@storage/src/main/java/org/zstack/storage/snapshot/group/VolumeSnapshotGroupTreeBuilder.java`
around lines 267 - 281, In markCurrent (class VolumeSnapshotGroupTreeBuilder)
the current selection is non-deterministic when all
VolumeSnapshotGroupTreeInventory.getCreateDate() values are null because newest
falls back to the first Map iteration; modify markCurrent to first detect
whether any node has a non-null createDate and only setNewest/setCurrent when at
least one non-null exists OR implement a stable tie-breaker: compare createDate
first, and when both are null or equal use a deterministic key such as
node.getUuid() (lexicographic ascending) to choose newest before calling
setCurrent(true) on the chosen VolumeSnapshotGroupTreeInventory; ensure you
reference groupNodeMap and update only the single chosen node.
Add groupTrees field to APIQueryVolumeSnapshotTreeReply, built from
VolumeSnapshotGroupVO topology with vote-majority parent resolution.
Extract build logic into VolumeSnapshotGroupTreeBuilder.
Resolves: ZSV-9792
Change-Id: I71616f6f6f6c637a6167637863727575746f616c
sync from gitlab !10007